package com.wulin.wucut.domain.entity.draft.content;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.wulin.wucut.common.JianYinConstant;
import com.wulin.wucut.domain.entity.draft.base.RangeBase;
import com.wulin.wucut.domain.entity.draft.base.VideoInfo;
import com.wulin.wucut.domain.entity.draft.content.materials.Segment;
import com.wulin.wucut.domain.entity.draft.content.materials.Videos;
import com.wulin.wucut.properties.JianYinDraftProperties;
import com.wulin.wucut.utils.JianYinUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FrameGrabber;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.wulin.wucut.common.JianYinConstant.*;

@Data
@Slf4j
public class DraftContent {
    private static final String MATERIAL_USE_RECORD = "materialUseRecord.json";
    private JSONObject draftContent;
    private JianYinDraftProperties properties;
    public DraftContent() {
        this.draftContent = JianYinUtil.getDraftContent();
        properties = SpringUtil.getBean(JianYinDraftProperties.class);
    }
    
    public DraftContent(String draftName){
        this.draftContent = JianYinUtil.getDraftContent(draftName);
        //TODO 首图背景模糊设置1
//        Integer blur = JianYinUtil.getRandomBlur();
//        Canvases canvases = appendCanvases(blur);
//        JSONArray canvasesList = getCanvases();
//        JSONArray mainSegments = getMainSegments();
//        canvasesList.add(JSONObject.parseObject(canvases.toString()));
//        JSONObject lastSegment = (JSONObject) mainSegments.get(mainSegments.size() - 1);
//        JSONArray materialRefs = lastSegment.getJSONArray("extra_material_refs");
//        materialRefs.add(canvases.getId());

    }
    
    public void start() throws IOException {

        Map<String, Integer> record = getMaterialUseRecord(MATERIAL_USE_RECORD);
        //读取改目录下的所有文件
        List<String> filePath = JianYinUtil.getAllFiles();
        //根据权重排序
        filePath.forEach(path->{
            record.putIfAbsent(path, 0);
        });
        Map<String, Integer> sortMap = MapUtil.sortByValue(record, false);
        List<String> sortFile = new ArrayList<>(sortMap.keySet());
        //打乱顺序
//        Collections.shuffle(filePath);
        for (String item : sortFile) {
            
            Videos videos = appendVideo(item);
            DraftContent content = appendMainSegment(videos);
            //随机变速 
            DraftContent turnSpeed = content.turnSpeed();
            //多次分割
            DraftContent split = turnSpeed.split();
            record.put(item, record.getOrDefault(item, 0) + 1);
            if (split.getDraftTime() < split.getMainSegmentTime()) {
                break;
            }
        }
        //TODO 写入json
        FileUtil.writeUtf8String(JSONObject.toJSONString(record), MATERIAL_USE_RECORD);
        
        //输出
        flush();

    }

    /**
     * 记录素材使用次数
     * @param fileName
     */
    public Map<String,Integer> getMaterialUseRecord(String fileName) throws IOException {
        File file = FileUtil.newFile(fileName);
        if (!file.exists()) {
             file.createNewFile();
        }
        String read = FileUtil.readUtf8String(file);
        return ObjectUtil.defaultIfNull(JSONObject.parseObject(read, Map.class),new HashMap<>()) ;
    }

    public void start(String draftFileName) throws IOException {
        String recordFilePath = Paths.get("src/main/resources", MATERIAL_USE_RECORD).toAbsolutePath().toString();
        Map<String, Integer> record = getMaterialUseRecord(recordFilePath);
        //读取改目录下的所有文件
        List<String> filePath = JianYinUtil.getAllFiles();
        List<String> sortFile = new ArrayList<>();
        if (properties.getEnableVideoWight()) {
            sortByWight(record, filePath, sortFile);
        }else {
            sortFile.addAll(filePath);
            Collections.shuffle(sortFile);
        }
        //打乱顺序
        for (String item : sortFile) {
            //防重复
            List<String> existsFiles = getMainSegmentMaterialNames();
            if (existsFiles.contains(item)) {
                continue;
            }
            Videos videos = appendVideo(item);
            DraftContent content = appendMainSegment(videos);
            //随机变速 
            DraftContent turnSpeed = content.turnSpeed();
            //多次分割
            DraftContent split = turnSpeed.split();
            //记录使用次数
            record.put(item, record.getOrDefault(item, 0) + 1);
            //TODO 模糊
            if (split.getDraftTime() < split.getMainSegmentTime()) {
                break;
            }
        }
        FileUtil.writeUtf8String(JSONObject.toJSONString(record), recordFilePath);
        //输出
        flush(draftFileName);

    }

    private void sortByWight(Map<String, Integer> record, List<String> filePath, List<String> sortFile) {
        //根据权重排序
        filePath.forEach(path->{
            record.putIfAbsent(path, 0);
        });
        //拿传入的文件素材目录进行遍历，而不是记录中的进行遍历
        // 1 使用次数低的优先 2 使用次数相同的，随即取
        //排除掉开头的
        Map<String, Integer> fileMap = filePath.stream()
                .filter(path->!getSegmentVideoNames().contains(path))
                .collect(Collectors.toMap(Function.identity(), record::get));

        Map<Integer, List<String>> valueOfMap = fileMap.entrySet()
                .stream()
                .collect(Collectors.groupingBy(Map.Entry::getValue,
                        Collectors.mapping(Map.Entry::getKey, Collectors.toList()))
                );

        MapUtil.sort(valueOfMap)
                .forEach((k, v) -> {
                    Collections.shuffle(v);
                    sortFile.addAll(v);
                });
    }

    public Videos appendVideo(String path) throws FrameGrabber.Exception {
        Integer randomRange = JianYinUtil.getRandomRange();
        VideoInfo info = new VideoInfo(path);
        Videos video = new Videos();
        video.setPath(path);
        video.setId(JianYinUtil.getId());
        video.setWidth(info.getWidth());
        video.setHeight(info.getHeight());
        video.setDuration(info.getDuration() * randomRange / 100);
        video.setType(JianYinConstant.VIDEO);
        JSONObject materials = draftContent.getJSONObject(JianYinConstant.MATERIALS);
        JSONArray videos = materials.getJSONArray(JianYinConstant.VIDEOS);
        JSONObject appendVideo = JSONObject.parseObject(JSONObject.toJSONString(video));
        videos.add(appendVideo);
        materials.put(JianYinConstant.VIDEOS, videos);
        draftContent.put(JianYinConstant.MATERIALS, materials);
        return video;
    }

    //将视频追加到主轨道
    public DraftContent appendMainSegment(Videos video) {
        JSONArray mainSegments = getMainSegments();
        JSONObject lastSegment = (JSONObject) mainSegments.get(mainSegments.size() - 1);
        JSONObject lastTargetTimeRange = lastSegment.getJSONObject(JianYinConstant.TARGET_TIME_RANGE);
        Long lastDuration = lastTargetTimeRange.getLong(DURATION);
        Long lastStart = lastTargetTimeRange.getLong(START);
        RangeBase sourceTimeRange = new RangeBase();
        sourceTimeRange.setStart(0L);
        sourceTimeRange.setDuration(video.getDuration());
        RangeBase targetTimeRange = new RangeBase();
        targetTimeRange.setDuration(video.getDuration());
        targetTimeRange.setStart(lastStart + lastDuration);
        //模拟第一个片段的行为
        JSONObject firstSegment = mainSegments.getJSONObject(0);
        JSONObject clip = firstSegment.getJSONObject("clip");
        
        Segment segment = new Segment();
        segment.setMaterialId(video.getId());
        segment.setSourceTimeRange(sourceTimeRange);
        segment.setTargetTimeRange(targetTimeRange);
        segment.setMaterialId(video.getId());
        segment.setExtraMaterialRefs(new ArrayList<>());
        //TODO 
        JSONObject seg = JSONObject.parseObject(JSONObject.toJSONString(segment));
        seg.put("clip", JSONObject.parseObject(JSONObject.toJSONString(clip)));
        mainSegments.add(seg);
        return this;
    }

    //随机切割最后一个片段,切割到不能切割为止
    public DraftContent split() {
        JSONArray mainSegments1 = getMainSegments();
        JSONObject lastSegment1 = (JSONObject) mainSegments1.get(mainSegments1.size() - 1);
        Double speed = lastSegment1.getDouble(SPEED);
        Integer blur = JianYinUtil.getRandomBlur();
        //原视频的时长
        while (true) {
            JSONArray mainSegments = getMainSegments();
            JSONArray videos = getVideos();
            //倒数第一个segment
            JSONObject lastSegment = (JSONObject) mainSegments.get(mainSegments.size() - 1);
            //倒数第一个video
            JSONObject lastVideo = (JSONObject) videos.get(videos.size() - 1);
            //复制
            JSONObject cloneSegment =   JSONObject.parseObject(lastSegment.toString()) ;
            Long randomSplit = JianYinUtil.getRandomSplit();
            RangeBase lastSegmentRanger = lastSegment.getObject(TARGET_TIME_RANGE, RangeBase.class);
            JSONObject startSegmentTimeRange = lastSegment.getJSONObject(SOURCE_TIME_RANGE);
            Long sourceStart = startSegmentTimeRange.getLong(START);
            Long sourceDuration = startSegmentTimeRange.getLong(DURATION);
            Long videoDuration = lastVideo.getLong(DURATION);
            Long duration = lastSegmentRanger.getDuration();
            JSONObject jsonObject = lastSegment.getJSONObject(TARGET_TIME_RANGE);
            JSONArray canvasesList = getCanvases();
            //处理画布
            Canvases canvases = appendCanvases(blur);
            canvasesList.add(JSONObject.parseObject(canvases.toString()));
            JSONArray materialRefs = lastSegment.getJSONArray("extra_material_refs");
            materialRefs.add(canvases.getId());
            //处理位置
            JSONObject clip = lastSegment.getJSONObject("clip");
            if (properties.getEnableLocation()) {
                JSONObject transform = clip.getJSONObject("transform");
                transform.put("x",  JianYinUtil.getRandomXLocation());
                transform.put("y", JianYinUtil.getRandomYLocation());
               
            }
            if (properties.getEnableScale()) {
                JSONObject scale = clip.getJSONObject("scale");
                double scaleValue = JianYinUtil.getRandomScale();
                log.info("scaleValue:{}", scaleValue);
                scale.put("x", scaleValue/100);
                scale.put("y", scaleValue/100);
            }
            lastSegment.put("clip", clip);
            if (videoDuration - sourceStart <= randomSplit) {
                //随机切割素材已经是最小,不能在进行切割
                jsonObject.put(DURATION, duration / speed);
                break;
            }
            JSONObject cloneVideo = (JSONObject) lastVideo.clone();
            String videoId = JianYinUtil.getId();
            cloneVideo.put("id", videoId);
            videos.add(cloneVideo);
            //开始切割
            jsonObject.put(DURATION, randomSplit / speed);
            startSegmentTimeRange.put(DURATION, randomSplit);
            //拼接尾部
            Long start = lastSegmentRanger.getStart();
            long end = start + Math.round(randomSplit / speed);
            long splitDuration = duration - randomSplit;
            cloneSegment.put(MATERIAL_ID, videoId);
            cloneSegment.put("id", JianYinUtil.getId());
            cloneSegment.put("extra_material_refs", new ArrayList<>());
            RangeBase targetRangeBase = new RangeBase();
            targetRangeBase.setStart(end);
            targetRangeBase.setDuration(splitDuration);
            cloneSegment.put(TARGET_TIME_RANGE, JSONObject.toJSON(targetRangeBase));
            RangeBase sourceRangeBase = new RangeBase();
            sourceRangeBase.setDuration(duration);
            sourceRangeBase.setStart(sourceStart + randomSplit);
            cloneSegment.put(SOURCE_TIME_RANGE, JSONObject.toJSON(sourceRangeBase));
            mainSegments.add(cloneSegment);
            //切割完成
        }
        return this;
    }

    /**
     * 整体变速：1 先变速后切割
     * 2：先切割后变速
     * 这里先采用先变速后切割比较简单
     *
     * @return
     */
    public DraftContent turnSpeed() {
        JSONObject materials = draftContent.getJSONObject(MATERIALS);
        Double randomSpeed = JianYinUtil.getRandomSpeed();
        JSONArray mainSegments = getMainSegments();
        JSONObject lastSegment = (JSONObject) mainSegments.get(mainSegments.size() - 1);
//        JSONObject targetTimerRange = lastSegment.getJSONObject(TARGET_TIME_RANGE);
//        JSONObject sourceTimeRanger = lastSegment.getJSONObject(SOURCE_TIME_RANGE);
//        Long targetDuration = targetTimerRange.getLong(DURATION);
//        Long sourceDuration = sourceTimeRanger.getLong(DURATION);
//        long targetSpeedDuration = Math.round(targetDuration / randomSpeed);
//        long sourceSpeedDuration = Math.round(sourceDuration);
//        targetTimerRange.put(DURATION, targetSpeedDuration);
//        sourceTimeRanger.put(DURATION, sourceSpeedDuration);
//        lastSegment.put(TARGET_TIME_RANGE, targetTimerRange);

        lastSegment.put("speed", randomSpeed);
        log.info("变速: speed:{}", randomSpeed);
        return this;

    }


    public JSONArray getVideos() {
        JSONObject materials = draftContent.getJSONObject(JianYinConstant.MATERIALS);
        return materials.getJSONArray(JianYinConstant.VIDEOS);
    }


    public JSONArray getTracks() {
        return draftContent.getJSONArray(JianYinConstant.TRACKS);
    }

    public JSONObject getMainTrack() {
        JSONArray tracks = getTracks();
        for (Object track : tracks) {
            JSONObject track1 = (JSONObject) track;
            int flag = track1.getIntValue(JianYinConstant.FLAG);
            if (0 == flag) {
                return track1;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        double i = 550 * 0.000925925925925926;
        double j = 0 * 0.000925925925925926;
        System.out.println(j);
    }


    public JSONArray getMainSegments() {
        JSONObject mainTrack = getMainTrack();
        return mainTrack.getJSONArray(JianYinConstant.SEGMENTS);
    }

    public JSONArray getCanvases() {
        JSONObject materials = draftContent.getJSONObject(MATERIALS);
        return materials.getJSONArray("canvases");
    }


    public void flush() {
        FileUtil.writeUtf8String(JSONObject.toJSONString(draftContent, SerializerFeature.DisableCircularReferenceDetect), JianYinUtil.getDraftContentPath());
    }
    public void flush(String draftName) {
        FileUtil.writeUtf8String(JSONObject.toJSONString(draftContent, SerializerFeature.DisableCircularReferenceDetect), JianYinUtil.getDraftContentPath(draftName));
    }

    /**
     * 获取主轨道时间
     *
     * @return
     */
    public Long getMainSegmentTime() {
        JSONArray mainSegments = getMainSegments();
        JSONObject lastSegment = (JSONObject) mainSegments.get(mainSegments.size() - 1);
        JSONObject targetTimeRange = lastSegment.getJSONObject(TARGET_TIME_RANGE);
        Long duration = targetTimeRange.getLong(DURATION);
        Long start = targetTimeRange.getLong(START);
        return duration + start;
    }


    public Long getDraftTime() {
        return draftContent.getLong(DURATION);
    }

    /**
     * 追加画布 背景
     *
     * @return
     */
    public Canvases appendCanvases(Integer blur) {
        String id = JianYinUtil.getId();
        Canvases canvases = new Canvases(id, blur);
        JSONObject materials = draftContent.getJSONObject("materials");
        JSONArray canvasesList = materials.getJSONArray("canvases");
        canvasesList.add(JSONObject.parseObject(canvases.toString()));
        return canvases;
    }
    
    
    public List<String> getMainSegmentMaterialNames() {
        JSONArray mainSegments = getMainSegments();
        JSONArray videos = getVideos();
        List<String> fileNames = new ArrayList<>();
        videos.forEach(v->{
            JSONObject video = (JSONObject) v;
            String fileName = video.getString("material_name");
            fileNames.add(fileName);
        });
        return fileNames;
    }


    /**
     * 找到主轨道已添加的视频名称
     * @return
     */
    public List<String> getSegmentVideoNames() {
        return getSegmentVideoId().stream().distinct()
                .map(this::getVideoPathById)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList());
    }

    public List<String> getSegmentVideoId() {
        List<String> list = new ArrayList<>();
        JSONArray mainSegments = getMainSegments();
        for (Object segment : mainSegments) {
            JSONObject s = (JSONObject) segment;
            list.add(s.getString("material_id"));
        }
        return list;
    }
    public String getVideoPathById(String videoId) {
        JSONArray videos = getVideos();
        for (Object video : videos) {
            if ((StrUtil.equals(videoId, ((JSONObject) video).getString("id")))) {
                return ((JSONObject) video).getString("path");
            }
        }
        return null;
    }


}
